<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <title>虚拟列表</title>
  <style>
    html,
    body {
      box-sizing: border-box;
    }

    .v-scroll {
      height: 600px;
      width: 400px;
      border: 3px solid #000;
      overflow: auto;
      position: relative;
      -webkit-overflow-scrolling: touch;
    }

    .infinite-list {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      z-index: -1;
    }

    .scroll-list {
      left: 0;
      right: 0;
      top: 0;
      position: absolute;
      text-align: center;
    }

    .scroll-item {
      padding: 10px;
      color: #555;
      box-sizing: border-box;
      border-bottom: 1px solid #999;
    }

    .border {
      border: 1px solid #000;
    }
  </style>
</head>

<body>
  <!-- 左边的元素使用 flex: 0 0 15% 设置为固定的宽度，同时使用 min-width 属性设置最小宽度，以防止它缩小到一定程度。 -->
  <div style="display: flex;">
    <div style="flex: 0 0 15%; min-width: 150px;max-width: 250px;" class="border">
      左边的元素
    </div>
    <div style="flex: 1 1 0%;" class="border">
      右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素右边的元素
    </div>
  </div>

  <div id="app">
    <div ref="list" class="v-scroll" @scroll="scrollEvent($event)">
      <div class="infinite-list" :style="{ height: listHeight + 'px' }"></div>

      <div class="scroll-list" :style="{ transform: getTransform }">
        <div ref="items" class="scroll-item" v-for="item in visibleData" :key="item.id"
          :style="{ height: itemHeight + 'px',lineHeight: itemHeight + 'px' }">{{ item.msg }}</div>
      </div>
    </div>
  </div>

  <script>
    {

    }
    {

    }
    {

    }
    {

    }
    {

    }
    {
      var a = {};
      var b = {
        key: 'a'
      };
      var c = {
        key: 'c'
      }
      a[b] = '123';
      a[c] = '456';
      console.log(a[b]);// '456'
      console.log(a[c]);// '456'
      console.log(a);// {[object Object]: "456"}
    }
    {
      var a = 5;
      var b = 6;
      // 如何不借助第三个变量完成上面的交换

      // 第一种
      {
        a = a + b;
        b = a - b;
        a = a - b;
      }
      // 第二种(只能数字？？？)
      {
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
      }
      // 第三种
      {
        [b, a] = [a, b]
      }
    }
    {
      /* 
        Symbol 类型的键在使用 Object.keys()、Object.entries() 和 JSON.stringify() 方法时会被忽略。
      */
      const isEmptyObject_0 = (obj) => Reflect.ownKeys(obj).length === 0;
      const isEmptyObject_1 = (obj) => Object.keys(obj).length === 0;
      const isEmptyObject_2 = (obj) => Object.entries(obj).length === 0;
      const isEmptyObject_3 = (obj) => Object.getOwnPropertyNames(obj).length === 0;
      const isEmptyObject_4 = (obj) => JSON.stringify(obj) === '{}';
      const isEmptyObject_5 = (obj) => JSON.stringify(obj) === '{}';
      function isEmptyObject_6(obj) {
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            return false;
          }
        }
        return true;
      }
      function isEmptyObject_7(obj) {
        let flag = true;
        for (let key in obj) {
          if (key) {
            flag = false;
            break;
          }
        }
        return flag;
      }

      const emptyObj = {};
      const nonEmptyObj = { name: 'John', age: 25 };
      const objWithSymbol = { [Symbol('key')]: 'value' };

      console.log(isEmptyObject_0(emptyObj), isEmptyObject_0(nonEmptyObj), isEmptyObject_0(objWithSymbol)); // 输出：true false false
      console.log(isEmptyObject_1(emptyObj), isEmptyObject_1(nonEmptyObj), isEmptyObject_1(objWithSymbol)); // 输出：true false true
      console.log(isEmptyObject_2(emptyObj), isEmptyObject_2(nonEmptyObj), isEmptyObject_2(objWithSymbol)); // 输出：true false true
      console.log(isEmptyObject_3(emptyObj), isEmptyObject_3(nonEmptyObj), isEmptyObject_3(objWithSymbol)); // 输出：true false true
      console.log(isEmptyObject_4(emptyObj), isEmptyObject_4(nonEmptyObj), isEmptyObject_4(objWithSymbol)); // 输出：true false true
      console.log(isEmptyObject_5(emptyObj), isEmptyObject_5(nonEmptyObj), isEmptyObject_5(objWithSymbol)); // 输出：true false true
      console.log(isEmptyObject_6(emptyObj), isEmptyObject_6(nonEmptyObj), isEmptyObject_6(objWithSymbol)); // 输出：true false true
      console.log(isEmptyObject_7(emptyObj), isEmptyObject_7(nonEmptyObj), isEmptyObject_7(objWithSymbol)); // 输出：true false true
      // 下面是jq里面的方法['1.11.3','3.6.4'] 返回结果都是true
      // const objWithSymbol = { [Symbol('key')]: 'value' };
      // console.log($.isEmptyObject(objWithSymbol));// true


    }
    {
      function A() { }
      function B() { }
      function create() {
        if (Math.random() > 0.5) {
          return new A();
        } else {
          return new B();
        }
      }
      let obj = create();
      // 下面当Math.random()<0.5时
      console.log(obj instanceof A);// false
      console.log(obj instanceof B);// true
      // 下面获取执行函数的名称
      console.log(obj.__proto__.constructor.name);// B

    }
    {
      /*  vue mixin的使用，查看下其它的用法记录到文档
      <template>
        <div>
          <button @click="increment">{{ count }}</button>
        </div>
      </template>
      
      <script>
      const CounterMixin = {
        data() {
          return {
            count: 0,
          };
        },
        methods: {
          increment() {
            this.count++;
          },
        },
      };
      
      export default {
        mixins: [CounterMixin], // 引入 mixin 对象
        // 组件选项
      };
  </script>
  */
  }
  {
  console.log(2 && 4);// 4
  console.log(2 || 4);// 2
  console.log(1 < 2 && 4> 5);// false
    console.log(1 < 2 || 4> 5);// true
      let obj = {};
      console.log(obj.a || 'undefined');// undefined
      }
      {
      /*
      type和interface的区别
      */
      }
      {
      /* 重排和重绘的区别
      重排（reflow）是指当页面的布局和几何属性发生改变时，浏览器会重新计算元素的大小和位置，然后重新构建页面的布局树和绘制树。重排是一个相对较慢的操作，因为它会涉及到重新计算多个元素的几何属性，并且可能会引发其他元素的重排。

      一些常见的导致重排的操作包括：

      修改元素的尺寸（宽度、高度）
      修改元素的位置（top、left）
      修改元素的边距（margin）
      修改元素的字体大小
      修改元素的样式（颜色、背景等）
      重绘（repaint）是指当元素的外观或样式发生改变时，浏览器会重新绘制元素的可视部分。重绘不会影响页面的布局，只会更新元素的外观。相对于重排，重绘的开销较小。

      一些常见的导致重绘的操作包括：

      修改元素的背景颜色
      修改元素的文本颜色
      修改元素的边框样式
      为了提高性能，减少页面的重排和重绘，可以采取以下措施：

      使用 CSS3 动画和过渡，它们通常比 JavaScript 动画性能更好，因为它们在浏览器的合成线程上运行，而不会引发重排和重绘。
      将需要多次修改的样式属性合并为一个操作，以减少重排和重绘的次数。
      使用 transform 属性来进行元素的平移、旋转和缩放，它可以在不引发重排的情况下修改元素的外观。
      尽量避免在布局树中的每个节点上使用复杂的选择器，这可能导致浏览器重新计算样式，引发重排。
      总结：

      重排是浏览器重新计算元素的布局和几何属性，而重绘是重新绘制元素的外观。
      重排比重绘开销更大，因为它会涉及到重新计算多个元素的属性和布局。
      为了提高性能，应该尽量减少页面的重排和重绘次数。
      */
      }
      {
      /* ts symbol类型 */
      }
      {
      /*
      call和apply的区别

      */
      }
      {
      /*
      vue history模式下部署服务器要注意什么问题

      */
      }
      {
      /*
      vue3 defineAsyncComponent 的使用示例
      import { defineAsyncComponent } from 'vue';

      const AsyncComponent = defineAsyncComponent(() =>
      import('./components/AsyncComponent.vue')
      );

      export default {
      components: {
      AsyncComponent,
      },
      };



      <template>
        <div>
          <h1>Parent Component</h1>
          <AsyncComponent />
        </div>
      </template>

      */

      }
      {
      function fetchFromAPI() {
      return new Promise((resolve, reject) => {
      // 模拟操作失败
      setTimeout(() => {
      reject(new Error('API request failed'));
      }, 1000);
      });
      }

      retryOperation(fetchFromAPI, 3, 1000)
      .then(result => {
      console.log('Operation succeeded:', result);
      })
      .catch(error => {
      console.error('Operation failed:', error);
      });

      }
      {
      /*
      js实现一个方法，失败重试

      */
      async function retryOperation(operation, maxAttempts, delay) {
      let attempts = 0;
      while (attempts < maxAttempts) { try { // 执行操作 const result=await operation(); return result; } catch (error) {
        console.error(`Attempt ${attempts + 1} failed:`, error); attempts++; await new Promise(resolve=>
        setTimeout(resolve, delay)); // 延迟一段时间后重试
        }
        }
        throw new Error(`Operation failed after attempts.`);
        }

        }
        {
        /*
        vue3 中tree shaking
        在 Vue 3 中，Tree Shaking 是一种优化技术，用于从最终的构建包中删除未使用的代码，以减小包的大小。它可以通过静态分析来检测和删除未使用的模块、函数、变量等，从而减少最终的项目体积。

        Vue 3 的 Tree Shaking 遵循标准的 ES 模块语法和工具链的支持。在使用 Vue 3 进行开发时，你可以利用以下几种方式来优化 Tree Shaking：

        使用 ES 模块语法：在编写代码时，尽可能使用 ES 模块语法（import 和 export）来导入和导出模块。这样可以帮助工具链更好地进行静态分析和优化。

        使用按需引入：Vue 3 的核心库（@vue/runtime-core）提供了按需引入的功能，可以根据需要只引入所使用的功能模块。这样可以避免引入未使用的模块，减小最终构建包的大小。

        例如，可以使用 import { createApp, ref } from 'vue' 来只引入 createApp 和 ref，而不引入其他未使用的功能模块。

        避免副作用：在编写代码时，尽量避免在模块中执行副作用操作，如直接修改全局变量、注册全局组件等。这样可以帮助工具链更好地进行静态分析和优化。

        配置工具链：如果你使用的是现代的打包工具（如 webpack），可以通过配置工具链来启用 Tree Shaking。确保在构建配置中启用了相应的 Tree Shaking 插件和配置选项。

        需要注意的是，Tree Shaking 的效果取决于工具链的支持和配置，以及代码的编写方式。在实际开发中，你可以使用工具链的构建分析工具来检查最终构建包的大小和组成，以确保 Tree Shaking 的有效性。

        */
        }
        {
        /*
        vue3中dom diff过程
        在 Vue 3 中，DOM Diff 过程有以下几个步骤：

        生成新的 VNode：根据组件模板或 render 函数生成新的 VNode 树。

        比较新旧 VNode：Vue 3 使用了一种称为 PatchFlag 的新的标记系统来跟踪 VNode 的变化。在比较新旧 VNode 时，Vue 3 会根据 PatchFlag 来判断 VNode 是否具有可比较的属性。

        按需更新：根据比较结果，Vue 3 会对需要更新的部分进行按需更新，而不是全量更新整个组件。

        如果新旧 VNode 完全相同，则不会进行任何更新。
        如果新旧 VNode 不相同，但具有相同的父节点，则会进行更新操作，包括属性的更新、文本内容的更新等。
        如果新旧 VNode 不相同且没有相同的父节点，则会进行替换操作，将旧的 VNode 替换为新的 VNode。
        如果新旧 VNode 不相同，但具有相同的标签名，则会对子节点进行逐个比较和更新，而不会替换整个子节点。
        更新子节点：在更新子节点时，Vue 3 使用了一种称为 Keyed Diff 的算法来优化对子节点的比较和更新。Keyed Diff 算法会根据节点的 key 属性来进行比较和更新，以减少更新的操作。
        如果旧的子节点和新的子节点都具有 key 属性，则 Vue 3 会根据 key 属性进行比较和更新。
        如果旧的子节点或新的子节点没有 key 属性，则会使用一种称为 Iterating Sequence Algorithm 的算法来进行比较和更新。
        完成更新：最后，Vue 3 会完成所有需要更新的操作，并将更新后的 VNode 渲染到真实的 DOM 中。
        Vue 3 的 DOM Diff 过程相对于 Vue 2 有一些改进，包括使用 PatchFlag 进行标记和按需更新的优化。这些改进使得 Vue 3 在性能和效率上有所提升。

        */
        }
        {
        /*
        vue2中路由跳转有哪些方式，怎么接收参数以及优缺点

        */
        }
        {
        /*
        mixin作用和场景以及缺点

        Mixin 是一种在 Vue 中重用组件逻辑的方式。它允许将一组选项（如数据、计算属性、方法等）混入到多个组件中，以实现代码的复用。Mixin 可以用于以下场景：

        作用：

        代码复用：Mixin 可以将一组逻辑和功能集中到一个地方，并在多个组件中重复使用。这可以减少代码的冗余，提高开发效率。
        功能扩展：Mixin 可以用于扩展组件的功能。通过混入一些常用的方法或计算属性，可以为多个组件提供共享的功能。
        逻辑抽象：Mixin 可以将一些通用的逻辑抽象出来，提供给多个组件使用。这可以使组件更加简洁和可读。
        场景：

        共享逻辑：当多个组件需要共享相同的逻辑时，可以使用 Mixin 将这些逻辑提取出来，并在多个组件中混入。
        通用功能：当有一些通用的功能需要在多个组件中使用时，可以使用 Mixin 将这些功能封装为一个 Mixin，并在需要的组件中混入。
        代码复用：当多个组件之间存在一些重复的代码时，可以使用 Mixin 将这些代码提取出来，并在多个组件中复用。
        缺点： 尽管 Mixin 提供了一种方便的代码复用机制，但过度使用 Mixin 可能会导致一些问题：

        命名冲突：当多个 Mixin 中具有相同的属性或方法时，可能会导致命名冲突，使代码难以理解和维护。
        依赖关系不清晰：当一个组件使用了多个 Mixin 时，组件的逻辑和依赖关系可能会变得复杂，导致代码难以理解和调试。
        组件耦合度增加：过多的 Mixin 可能使组件之间的耦合度增加，使代码的维护和扩展变得困难。
        因此，在使用 Mixin 时需要注意适度使用，确保代码的可读性、可维护性和可扩展性

        */
        }
        {
        /*
        对象的呢
        在 Vue 2 中，当操作对象时，一些对象的属性操作会触发视图的更新，而另一些则不会触发。下面是一些常见的对象属性操作的情况：

        会触发更新的对象属性操作：

        直接通过赋值语句修改对象的属性值，例如：obj.property = value。
        使用 Vue.set 或 vm.$set 方法向对象添加新的属性，并给属性赋值，例如：Vue.set(obj, 'newProperty', value) 或 vm.$set(obj, 'newProperty',
        value)。
        使用 Object.assign 或 spread 运算符创建一个新的对象，并赋值给原始对象的引用，例如：obj = Object.assign({}, obj, { newProperty: value }) 或 obj
        = { ...obj, newProperty: value }。
        这些对象属性操作会修改对象的属性值或对象的引用，从而触发 Vue 进行重新渲染。

        不会触发更新的对象属性操作：

        直接通过赋值语句修改对象的属性值，但属性值与之前相同，例如：obj.property = obj.property。
        使用 delete 关键字删除对象的属性，例如：delete obj.property。
        使用 Object.freeze 方法冻结对象，使其属性不可修改。
        这些对象属性操作不会修改对象的属性值或对象的引用，因此不会触发 Vue 进行重新渲染。

        需要注意的是，Vue 2 中的响应式系统是基于 JavaScript 对象的 getter 和 setter 实现的，当通过 getter 获取对象的属性时，Vue 会追踪这个属性的依赖关系，并在属性发生变化时触发更新
        */
        }
        {
        /*
        vue2 数组的哪些属性操作会更新，哪些不会
        在 Vue 2 中，当操作数组时，一些数组的属性操作会触发视图的更新，而另一些则不会触发。下面是一些常见的数组属性操作的情况：

        会触发更新的数组属性操作：

        push(): 向数组末尾添加元素。
        pop(): 从数组末尾移除元素。
        shift(): 从数组开头移除元素。
        unshift(): 向数组开头添加元素。
        splice(): 在指定位置插入、删除或替换元素。
        sort(): 对数组进行排序。
        reverse(): 反转数组的顺序。
        这些数组属性操作会修改原始数组，从而触发 Vue 进行重新渲染。

        不会触发更新的数组属性操作：

        slice(): 返回一个新的数组，不会修改原始数组。
        concat(): 返回一个新的数组，将原始数组与其他数组或值合并，但不会修改原始数组。
        filter(): 返回一个新的数组，根据指定的条件筛选原始数组中的元素，但不会修改原始数组。
        map(): 返回一个新的数组，根据指定的逻辑对原始数组中的元素进行转换，但不会修改原始数组。
        这些数组属性操作会返回一个新的数组，而不是直接修改原始数组，因此不会触发 Vue 进行重新渲染。

        需要注意的是，如果你使用 vm.$set 或 Vue.set 方法来对数组进行修改，Vue 会将其视为修改了数组，并触发更新。例如，vm.$set(array, index, value)
        可以用于在数组中指定的索引位置设置一个新值，并触发视图更新。

        */
        }
        {
        /*

        vue2 domdiff过程

        在 Vue 2 中，DOM Diff 是用于计算虚拟 DOM 树之间的差异，并将差异应用到实际 DOM 上的过程。下面是 Vue 2 中 DOM Diff 的大致过程：

        创建新的虚拟 DOM 树：在每次组件更新时，Vue 会先创建一个新的虚拟 DOM 树，表示组件的最新状态。

        比较新旧虚拟 DOM 树：Vue 会将新的虚拟 DOM 树与上一次更新时的旧虚拟 DOM 树进行比较，找出两者之间的差异。

        计算差异：在比较新旧虚拟 DOM 树时，Vue 会遍历两棵树的节点，并进行差异计算。Vue 使用了一种高效的算法叫做 Virtual DOM Diffing，该算法会尽量在最小的操作次数内找出最小的差异。

        生成差异补丁：在计算差异后，Vue 会生成一系列的差异补丁（patch）。每个差异补丁描述了需要对实际 DOM 进行的一项修改操作，如插入、更新或删除节点等。

        应用差异补丁：Vue 会将生成的差异补丁应用到实际的 DOM 上，从而实现视图的更新。Vue 使用了一种叫做 Patch 的机制，根据差异补丁的描述，按照一定的顺序对实际 DOM 进行修改。

        通过这个过程，Vue 2 可以高效地跟踪组件的状态变化，并将最小的修改应用到实际 DOM 上，从而实现高性能的视图更新。

        需要注意的是，Vue 2 中的 DOM Diff 是基于 Virtual DOM 的，而不是直接操作实际 DOM。Vue 2 通过比较和计算虚拟 DOM 树的差异，最终只会对实际 DOM 进行最小的操作，从而提高性能和效率。
        */
        }
        {
        /*
        props attr listeners 的作用和场景
        props：props 是用于从父组件向子组件传递数据的属性。父组件可以通过在子组件上定义 props 来声明要传递的数据，并通过属性的方式将数据传递给子组件。子组件可以使用这些 props
        来接收并使用父组件传递的数据。这种方式适用于父子组件之间的单向数据流通信。

        attrs：attrs 是用于将父组件上非 props 属性传递给子组件的属性。当父组件将属性传递给子组件时，如果子组件没有声明对应的 props，那么这些属性会被自动添加到子组件的 attrs 属性中。子组件可以通过
        $attrs 访问这些属性。这种方式适用于需要将一些额外的属性传递给子组件的场景。

        listeners：listeners 是用于将父组件上的事件监听器传递给子组件的属性。当父组件在子组件上使用 v-on 或 @ 来监听事件时，这些监听器会被自动添加到子组件的 listeners 属性中。子组件可以通过
        $listeners 访问这些事件监听器，并使用 $emit 来触发父组件上的事件。这种方式适用于子组件需要触发父组件中定义的事件的场景。

        这些属性在组件之间进行通信时非常有用，可以实现父子组件之间的数据传递和事件触发。它们的作用和使用场景如上所述。


        listeners的使用示例 找一个示例
        Event Bus的使用
        */
        }
        {
        /*
        vue2中组件通信的方式有哪些

        Props / $emit：通过 props 属性将数据从父组件传递给子组件，子组件可以通过 $emit 方法触发自定义事件来向父组件传递数据。这是最基本和常用的组件通信方式。

        Vuex：Vuex 是 Vue 的官方状态管理库，可以用于在不同组件之间共享和管理状态。通过在组件中派发 commit 或 dispatch 方法来修改或获取状态，组件可以实现跨层级的通信。

        $ref：通过 $ref 可以在父组件中引用子组件实例，并直接访问子组件的属性和方法。这种方式可以实现父组件主动调用子组件的操作。

        Event Bus：使用一个空的 Vue 实例作为事件总线，通过 $on 和 $emit 方法来进行组件之间的通信。任何一个组件都可以订阅事件或触发事件，从而实现组件之间的解耦。

        Provide / Inject：通过 provide 和 inject 可以在父组件中提供数据，在后代组件中注入并使用这些数据。这种方式可以实现跨层级的组件通信。

        $parent / $children：通过 $parent 和 $children 可以访问父组件和子组件的实例，从而实现组件之间的直接通信。但是这种方式不够灵活，并且破坏了组件的封装性。

        每种通信方式都有其适用的场景和注意事项。需要根据具体的需求选择合适的方式来实现组件之间的通信。
        */
        }
        {
        /* nextTick原理
        nextTick 是 Vue 中一个常用的方法，它用于在下次 DOM 更新循环结束之后执行延迟回调函数。它的原理是利用浏览器的事件循环机制，在当前代码执行完毕后，利用微任务或宏任务的方式来异步执行回调函数。

        具体来说，nextTick 的实现原理如下：

        首先，Vue 会先判断浏览器是否支持原生的 Promise 对象，如果支持，就使用 Promise 来创建一个微任务。

        如果浏览器不支持 Promise，Vue 会判断浏览器是否支持原生的 MutationObserver，如果支持，就使用 MutationObserver 来创建一个微任务。

        如果浏览器既不支持 Promise 也不支持 MutationObserver，Vue 会退回到使用 setTimeout 创建一个宏任务。

        无论使用哪种方式，nextTick 的原理都是将传入的回调函数放入一个任务队列中，并在下次事件循环中执行该任务队列。

        下面是一个示例代码，演示了如何使用 nextTick：
        */
        // 在 Vue 组件中使用 nextTick
        this.$nextTick(() => {
        // 在 DOM 更新循环结束后执行的回调函数
        // 可以在这里操作已更新的 DOM
        });

        // 在普通 JavaScript 中使用 nextTick
        Vue.nextTick(() => {
        // 在 DOM 更新循环结束后执行的回调函数
        // 可以在这里操作已更新的 DOM
        });
        /*
        需要注意的是，nextTick 并不是立即执行回调函数，而是在下次事件循环中执行。这意味着在调用 nextTick 后，不能立即获取到更新后的 DOM。如果需要在 DOM 更新后执行一些操作，可以将这些操作放在
        nextTick 的回调函数中。
        */
        }
        {
        // operation：一个异步函数，表示需要执行的操作。它应该返回一个 Promise 对象，用于处理操作的结果。
        // maxAttempts：最大重试次数。
        // delay：每次重试之间的延迟时间（毫秒）。
        async function retryOperation(operation, maxAttempts, delay) {
        let attempts = 0;
        while (attempts < maxAttempts) { try { // 执行操作 const result=await operation(); return result; } catch (error) {
          console.error(`Attempt ${attempts + 1} failed:`, error); attempts++; await new Promise(resolve=>
          setTimeout(resolve, delay)); // 延迟一段时间后重试
          }
          }
          throw new Error(`Operation failed after attempts.`);
          }
          function fetchFromAPI() {
          return new Promise((resolve, reject) => {
          // 模拟操作失败
          setTimeout(() => {
          reject(new Error('API request failed'));
          }, 1000);
          });
          }
          retryOperation(fetchFromAPI, 3, 1000)
          .then(result => {
          console.log('Operation succeeded:', result);
          })
          .catch(error => {
          console.error('Operation failed:', error);
          });
          }
          {
          function retryOperation(operation, maxAttempts, delay) {
          return new Promise((resolve, reject) => {
          let attempts = 0;

          function executeOperation() {
          attempts++;
          operation()
          .then(resolve)
          .catch(error => {
          if (attempts < maxAttempts) { console.log(`Attempt ${attempts} failed. Retrying in ${delay}ms...`);
            setTimeout(executeOperation, delay); } else { reject(`Exceeded maximum number of attempts
            (${maxAttempts}).`); } }); } executeOperation(); }); } function fetchData() { return new Promise((resolve,
            reject)=> {
            // 模拟一个异步操作
            setTimeout(() => {
            const success = Math.random() < 0.5; if (success) { resolve('Data fetched successfully.'); } else {
              reject('Failed to fetch data.'); } }, 1000); }); } retryOperation(fetchData, 3, 1000) .then(data=> {
              console.log(data);
              })
              .catch(error => {
              console.error(error);
              });

              }
              {
              /**
              * 非负整数共同富裕
              * 返回最新的数组
              */
              function CommonProsperity(arr = []) {
              let sum = arr.reduce((total, num) => total + num, 0);
              let len = arr.length;
              let res = sum / len;
              if (Number.isInteger(res)) {
              return Array(len).fill(res)
              } else {
              let newArr = Array(len).fill(Math.floor(res))
              function FillingOil() {
              newArr = newArr.map((item, index) => {
              if (arr[index] > item) {
              item += 1;
              }
              return item
              })
              let newSum = newArr.reduce((total, num) => total + num, 0);
              if (newSum < sum) { console.log(`index.html 81 ['keep']`, 'keep' ); FillingOil() } } FillingOil() return
                newArr } } console.log(CommonProsperity([5, 3, 1, 4, 2])); // 输出: [4, 3, 2, 4, 2]
                console.log(CommonProsperity([5, 4, 1, 4, 2])); // 输出: [4, 4, 2, 4, 2] console.log(CommonProsperity([5,
                5, 1, 4, 2])); // 输出: [4, 5, 2, 4, 2] console.log(CommonProsperity([5, 5, 2, 4, 2])); // 输出: [4, 5, 3,
                4, 2] console.log(CommonProsperity([5, 5, 3, 4, 2])); // 输出: [4, 5, 3, 4, 3]
                console.log(CommonProsperity([5, 5, 3, 4, 3])); // 输出: [4, 5, 4, 4, 3] } { // break 能跳出循环么 function
                adjustArray(arr) { while (true) { const max=Math.max(...arr); const min=Math.min(...arr); if (max -
                min===1 || arr.length===2) { break; } const maxIndex=arr.indexOf(max); arr[maxIndex] -=1;
                arr[arr.indexOf(min)] +=1; } return arr; } const originalArray=[5, 3, 1, 4, 2]; const
                adjustedArray=adjustArray(originalArray); console.log(adjustedArray); // [3, 3, 3, 3, 3] } { } /*
                思路：我们只要知道手机屏幕最多能放下几条数据，当下拉滑动时，通过双指针的方式截取相应的数据就可以了。 🚩 PS:为了防止滑动过快导致的白屏现象，我们可以使用预加载的方式多加载一些数据出来。 */ var
                throttle=(func, delay)=> { //节流
                var prev = Date.now();
                return function () {
                var context = this;
                var args = arguments;
                var now = Date.now();
                if (now - prev >= delay) {
                func.apply(context, args);
                prev = Date.now();
                }
                }
                }
                let listData = []
                for (let i = 1; i <= 10000; i++) { listData.push({ id: i, msg: i + ':' + Math.floor(Math.random() *
                  10000) }) } const { createApp }=Vue createApp({ data() { return { listData: listData, itemHeight: 60,
                  //可视区域高度 screenHeight: 600, //偏移量 startOffset: 0, //起始索引 start: 0, //结束索引 end: null, }; }, computed: {
                  //列表总高度 listHeight() { return this.listData.length * this.itemHeight; }, //可显示的列表项数 visibleCount() {
                  return Math.ceil(this.screenHeight / this.itemHeight) }, //偏移量对应的style getTransform() { return
                  `translate3d(0,${this.startOffset}px,0)`; }, //获取真实显示列表数据 visibleData() { return
                  this.listData.slice(this.start, Math.min(this.end, this.listData.length)); } }, mounted() {
                  this.start=0; this.end=this.start + this.visibleCount; }, methods: { scrollEvent() { //当前滚动位置 let
                  scrollTop=this.$refs.list.scrollTop; //此时的开始索引 this.start=Math.floor(scrollTop / this.itemHeight);
                  //此时的结束索引 this.end=this.start + this.visibleCount; //此时的偏移量 this.startOffset=scrollTop - (scrollTop %
                  this.itemHeight); } } }).mount('#app') </script>
</body>

</html>